#pragma once
#include "DTriMesh.h"
#include "../DMatrix4.h"
#include "../DVector3.h"
#include "DTriMeshData.h"
#include "../DSpace.h"
#include "DRay.h"

int _TriCallback(dGeomID TriMesh, dGeomID RefObject, int TriangleIndex)
{
	Skill::Ode::DIUnmanagedPointer^ p = Skill::Ode::DRegisteredObjects::Get(IntPtr(TriMesh));
	if(p)
	{
		DTriMesh^ triMesh = dynamic_cast<DTriMesh^>(p);
		if(triMesh->Callback)
		{
			p = Skill::Ode::DRegisteredObjects::Get(IntPtr(RefObject));
			DGeometry^ refObj = dynamic_cast<DGeometry^>(p);
			return triMesh->Callback(triMesh,refObj,TriangleIndex);
		}
	}
	return 0;
}
void _TriArrayCallback(dGeomID TriMesh, dGeomID RefObject, const int* TriIndices, int TriCount)
{
	Skill::Ode::DIUnmanagedPointer^ p = Skill::Ode::DRegisteredObjects::Get(IntPtr(TriMesh));
	if(p)
	{
		DTriMesh^ triMesh = dynamic_cast<DTriMesh^>(p);
		if(triMesh->ArrayCallback)
		{
			p = Skill::Ode::DRegisteredObjects::Get(IntPtr(RefObject));
			DGeometry^ refObj = dynamic_cast<DGeometry^>(p);

			array<int>^ arr = gcnew array<int>(TriCount);
			for(int i=0;i<TriCount;i++)
				arr[i] = TriIndices[i];
			triMesh->ArrayCallback(triMesh,refObj,arr);
		}
	}	
}
int _TriRayCallback(dGeomID TriMesh, dGeomID Ray, int TriangleIndex, dReal u, dReal v)
{
	Skill::Ode::DIUnmanagedPointer^ p = Skill::Ode::DRegisteredObjects::Get(IntPtr(TriMesh));
	if(p)
	{
		DTriMesh^ triMesh = dynamic_cast<DTriMesh^>(p);
		if(triMesh->RayCallback)
		{
			p = Skill::Ode::DRegisteredObjects::Get(IntPtr(Ray));
			DRay^ ray = dynamic_cast<DRay^>(p);
			return triMesh->RayCallback(triMesh,ray,TriangleIndex,u,v);
		}
	}
	return 0;
}

namespace Skill
{
	namespace Ode
	{
		namespace Geometries
		{		
			void DTriMesh::CollectManagedMemory()
			{
				_LastTransform = nullptr;
				DGeometry::CollectManagedMemory();
			}

			DTriMesh::DTriMesh(DSpace^ space, DTriMeshData^ data)
				:DGeometry(space)
			{
				dSpaceID sp = space? space->_Space() : nullptr;
				_SetPointer(dCreateTriMesh(sp,data->_This,0,0,0),true);
				_Data = data;
				if(_Container)
					_Container->AddSpace(this);
				callback = nullptr;
				arrayCallback = nullptr;
				rayCallback = nullptr;
			}

			DTriCallback^ DTriMesh::Callback::get()
			{
				if(IsSpace) return nullptr;
				return callback;
			}
			void DTriMesh::Callback::set(DTriCallback^ value)
			{
				if(IsSpace) return;
				callback = value;
				if(callback)
					dGeomTriMeshSetCallback(_Geom(),_TriCallback);
				else dGeomTriMeshSetCallback(_Geom(),0);
			}

			DTriArrayCallback^ DTriMesh::ArrayCallback::get()
			{
				if(IsSpace) return nullptr;
				return arrayCallback;
			}
			void DTriMesh::ArrayCallback::set(DTriArrayCallback^ value)
			{
				if(IsSpace) return;
				arrayCallback = value;
				if(arrayCallback)
					dGeomTriMeshSetArrayCallback(_Geom(),_TriArrayCallback);
				else dGeomTriMeshSetArrayCallback(_Geom(),0);
			}

			DTriRayCallback^ DTriMesh::RayCallback::get()
			{
				if(IsSpace) return nullptr;
				return rayCallback;
			}
			void DTriMesh::RayCallback::set(DTriRayCallback^ value)
			{
				if(IsSpace) return;
				rayCallback = value;
				if(arrayCallback)
					dGeomTriMeshSetRayCallback(_Geom(),_TriRayCallback);
				else dGeomTriMeshSetRayCallback(_Geom(),0);
			}			

			void DTriMesh::EnableTC(int geomClass, bool enable)
			{
				if(IsSpace) return;
				dGeomTriMeshEnableTC(_Geom(),geomClass, enable? 1 : 0);
			}
			bool DTriMesh::IsTCEnabled(int geomClass)
			{
				if(IsSpace) return false;
				return dGeomTriMeshIsTCEnabled(_Geom(),geomClass) == 1;
			}

			void DTriMesh::EnableTC(OdeClassses geomClass, bool enable)
			{
				if(IsSpace) return;
				dGeomTriMeshEnableTC(_Geom(),(int)geomClass, enable? 1 : 0);
			}
			bool DTriMesh::IsTCEnabled(OdeClassses geomClass)
			{
				if(IsSpace) return false;
				return dGeomTriMeshIsTCEnabled(_Geom(),(int)geomClass) == 1;
			}


			DMatrix4^ DTriMesh::LastTransform::get()
			{
				if(IsSpace) return nullptr;
				if(_LastTransform)
					_LastTransform->_SetPointer(dGeomTriMeshGetLastTransform(_Geom()),false);
				else
					_LastTransform = gcnew DMatrix4(dGeomTriMeshGetLastTransform(_Geom()));
				return _LastTransform;
			}
			void DTriMesh::LastTransform::set(DMatrix4^ value)
			{
				if(IsSpace) return;
				dGeomTriMeshSetLastTransform(_Geom(),value->_This);
			}

			int DTriMesh::TriangleCount::get()
			{
				if(IsSpace) return Int32::MinValue;
				return dGeomTriMeshGetTriangleCount(_Geom());
			}
			
			DTriMeshData^ DTriMesh::Data::get()
			{
				if(IsSpace) return nullptr;
				return _Data;
			}
			void DTriMesh::Data::set(DTriMeshData^ value)
			{
				if(IsSpace) return;
				_Data = value;
				dGeomTriMeshSetData(_Geom(),value->_This);
			}

			void DTriMesh::ClearTCCache()
			{
				if(IsSpace) return;
				dGeomTriMeshClearTCCache(_Geom());
			}

			void DTriMesh::GetTriangle(int index, [OutAttribute]DVector3^ %v0, [OutAttribute]DVector3^ %v1, [OutAttribute]DVector3^ %v2)
			{				
				v0 = gcnew DVector3();
				v1 = gcnew DVector3();
				v2 = gcnew DVector3();
				if(IsSpace) return;

				dVector3 v30;
				dVector3 v31;
				dVector3 v32;

				dGeomTriMeshGetTriangle(_Geom(),index,&v30,&v31,&v32);
				v0->_This[0] = v30[0]; v0->_This[1] = v30[1]; v0->_This[2] = v30[2];
				v1->_This[0] = v31[0]; v1->_This[1] = v31[1]; v1->_This[2] = v31[2];
				v2->_This[0] = v32[0]; v2->_This[1] = v32[1]; v2->_This[2] = v32[2];
			}
			DVector3^ DTriMesh::GetPoint(int Index, dReal u, dReal v)
			{
				if(IsSpace) return nullptr;
				dVector3 o;
				dGeomTriMeshGetPoint(_Geom(),Index,u,v,o);
				DVector3^ O = gcnew DVector3();
				O->_This[0] = o[0];
				O->_This[1] = o[1];
				O->_This[2] = o[2];
				return O;
			}
				
		}
	}
}